﻿// =============================================
// 文件保护宏：防止该头文件被重复包含（避免编译错误）
// 如果尚未定义 CUSTOM_BRDF_INCLUDED，则定义它并继续，否则跳过
// =============================================
#ifndef CUSTOM_BRDF_INCLUDED
#define CUSTOM_BRDF_INCLUDED

// =============================================
// 定义一个 BRDF（双向反射分布函数）结构体
// 用于在光照计算中封装 “漫反射” 和 “镜面反射” 的相关信息
// =============================================
struct BRDF {
	float3 diffuse;     // 漫反射颜色（通常为表面颜色 × (1 - 反射率)）
	float3 specular;    // 镜面反射颜色（高光部分，与金属度和反射率有关）
	float roughness;    // 表面粗糙度，用于控制镜面反射的模糊程度
};

// =============================================
// 定义最小反射率常量：即使是完全非金属，也会有一点微弱的反射（比如塑料）
// 这是为了避免非金属表面完全黑掉，更符合现实观察
// 通常取值 0.04，对应于常见非金属（如塑料、陶瓷）的最小菲涅尔反射
// =============================================
#define MIN_REFLECTIVITY 0.04

// =============================================
// 函数：OneMinusReflectivity
// 作用：根据材质的金属度（metallic），计算 “1 - 反射率”
// 即：有多少比例的光线会以漫反射形式被吸收/散射，而不是镜面反射
// 公式背后思想：金属几乎全部反射（高反射率），非金属反射很少（低反射率）
// =============================================
float OneMinusReflectivity(float metallic) {
	// 可反射范围是从 MIN_REFLECTIVITY（非金属最小反射） 到 1.0（金属）
	float range = 1.0 - MIN_REFLECTIVITY;

	// 最终结果：反射率 = metallic × range，那么 1 - 反射率就是：
	return range - metallic * range;
}

// =============================================
// 函数：GetBRDF
// 作用：根据输入的 Surface 数据，计算并返回一个 BRDF 结构体
// 该结构体包含：漫反射颜色、镜面反射颜色、表面粗糙度
// 这是 PBR 光照模型的核心输入之一
// =============================================
BRDF GetBRDF(Surface surface, bool applyAlphaToDiffuse = false) {
	BRDF brdf;

	// 计算 "1 - 反射率"，即漫反射占比
	float oneMinusReflectivity = OneMinusReflectivity(surface.metallic);

	// 漫反射颜色 = 表面颜色 × (1 - 反射率)
	// 即：非金属部分贡献的散射颜色
	brdf.diffuse = surface.color * oneMinusReflectivity;

	// 如果启用 applyAlphaToDiffuse，还将漫反射颜色乘以透明度（用于预乘透明等高级效果）
	if (applyAlphaToDiffuse) {
		brdf.diffuse *= surface.alpha;
	}

	// 镜面反射颜色 = 在 MIN_REFLECTIVITY（非金属最小反射） 与 表面颜色 之间插值
	// 插值系数是 metallic，即金属度越高，越接近表面颜色（高反射），否则接近 MIN_REFLECTIVITY
	brdf.specular = lerp(MIN_REFLECTIVITY, surface.color, surface.metallic);

	// 将 “感知上的光滑度（smoothness）” 转换为 “感知粗糙度（perceptual roughness）”
	float perceptualRoughness =
		PerceptualSmoothnessToPerceptualRoughness(surface.smoothness);

	// 再将 “感知粗糙度” 转换为 “物理粗糙度（roughness）”，用于 GGX / BRDF 公式
	brdf.roughness = PerceptualRoughnessToRoughness(perceptualRoughness);

	// 返回完整的 BRDF 数据
	return brdf;
}

// =============================================
// 函数：SpecularStrength
// 作用：计算当前光源下，该表面的 **镜面反射强度（Specular 强度）**
// 这是 Blinn-Phong / GGX 高光模型中非常重要的部分，与法线、光线方向、视线方向相关
// 返回一个标量（float），表示该光源对镜面反射的贡献权重
// =============================================
float SpecularStrength(Surface surface, BRDF brdf, Light light) {
	// 计算半角向量 h = (light.dir + viewDir) / 2，表示光线与视线的中间方向
	// 这是 Blinn-Phong / GGX 模型中用于计算高光的关键方向
	float3 h = SafeNormalize(light.direction + surface.viewDirection);

	// 计算法线与半角向量的点积的平方（nh），并限制在 [0,1] 范围内
	float nh2 = Square(saturate(dot(surface.normal, h)));

	// 计算光线方向与半角向量的点积的平方（lh），同样限制范围
	float lh2 = Square(saturate(dot(light.direction, h)));

	// 粗糙度的平方（用于 GGX 分母计算）
	float r2 = Square(brdf.roughness);

	// GGX NDF（法线分布函数）中分母的一部分：(nh² * (r² - 1) + 1)
	// 加上 1.00001 是为了避免除零，确保数值稳定
	float d2 = Square(nh2 * (r2 - 1.0) + 1.00001);

	// GGX 模型中的归一化系数：(roughness * 4 + 2)
	float normalization = brdf.roughness * 4.0 + 2.0;

	// 最终返回镜面强度：r² / (d² * max(0.1, lh²) * normalization)
	// 这是 GGX / Disney BRDF 高光项的常见形式之一
	return r2 / (d2 * max(0.1, lh2) * normalization);
}

// =============================================
// 函数：DirectBRDF
// 作用：计算 **直接光照（比如平行光、点光源）下，该表面受光源影响的最终 BRDF 颜色贡献**
// 返回值：float3，即 漫反射颜色贡献 + 镜面反射颜色贡献
// =============================================
float3 DirectBRDF(Surface surface, BRDF brdf, Light light) {
	// 总 BRDF 贡献 = 镜面强度 × 镜面颜色 + 漫反射颜色
	return SpecularStrength(surface, brdf, light) * brdf.specular + brdf.diffuse;
}

// =============================================
// 文件保护宏结束
// =============================================
#endif